﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DteExtensions.cs" company="Roman '4ux-nbIx' Novitsky">
//   Copyright (c) 2012 Roman '4ux-nbIx' Novitsky
// </copyright>
// <summary>
//   Defines <see cref="EnvDTE.DTE" /> class extension methods.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace MonoDevAssist
{
  #region Namespace Imports

  using System;
  using System.Collections.Generic;
  using System.Linq;

  using EnvDTE;

  #endregion


  /// <summary>
  /// Defines <see cref="EnvDTE.DTE"/> class extension methods.
  /// </summary>
  internal static class DteExtensions
  {
    #region Public Methods

    public static Project GetActiveProject(this _DTE dte)
    {
      Project activeProject = null;
      var activeSolutionProjects = dte.ActiveSolutionProjects as Array;

      if (activeSolutionProjects != null && activeSolutionProjects.Length > 0)
      {
        activeProject = activeSolutionProjects.GetValue(0) as Project;
      }

      return activeProject;
    }

    public static IEnumerable<CodeClass> GetClass(this _DTE dte, string className, IEnumerable<string> platforms)
    {
      foreach (Project project in GetSolutionProjects(dte))
      {
        if (project.CodeModel == null)
        {
          continue;
        }

        var supportedPlatforms = (Array)project.ConfigurationManager.SupportedPlatforms;
        if (!supportedPlatforms.Cast<string>().Union(platforms).Any())
        {
          continue;
        }

        foreach (CodeClass codeClass in FindClass(project.CodeModel.CodeElements, className))
        {
          yield return codeClass;
        }
      }
    }


    /// <summary>
    /// Gets the <see cref="EnvDTE.CodeType"/> .
    /// </summary>
    /// <param name="dte">
    /// The DTE. 
    /// </param>
    /// <param name="fullTypeName">
    /// Full name of the type. 
    /// </param>
    /// <param name="platforms">
    /// The platforms. 
    /// </param>
    /// <returns>
    /// An instance of <see cref="EnvDTE.CodeType"/> . 
    /// </returns>
    public static CodeType GetCodeType(this _DTE dte, string fullTypeName, IEnumerable<string> platforms)
    {
      var codeTypes = new List<CodeType>();

      foreach (Project project in GetSolutionProjects(dte))
      {
        if (project.CodeModel == null)
        {
          continue;
        }

        var supportedPlatforms = (Array)project.ConfigurationManager.SupportedPlatforms;
        if (!supportedPlatforms.Cast<string>().Union(platforms).Any())
        {
          continue;
        }

        CodeType codeType = project.CodeModel.CodeTypeFromFullName(fullTypeName);

        if (codeType != null)
        {
          codeTypes.Add(codeType);
        }
      }

      return codeTypes.Where(t => t.InfoLocation == vsCMInfoLocation.vsCMInfoLocationProject).FirstOrDefault()
             ?? codeTypes.Where(t => t.InfoLocation == vsCMInfoLocation.vsCMInfoLocationExternal).FirstOrDefault();
    }


    /// <summary>
    /// Gets the solution projects.
    /// </summary>
    /// <param name="dte">
    /// The DTE. 
    /// </param>
    /// <returns>
    /// The solution projects. 
    /// </returns>
    public static IEnumerable<Project> GetSolutionProjects(this _DTE dte)
    {
      var projects = new List<Project>();

      foreach (Project project in dte.Solution.Projects)
      {
        if (project.IsSolutionFolder())
        {
          projects.AddRange(project.GetSolutionFolderProjects());

          continue;
        }

        projects.Add(project);
      }

      return projects;
    }

    #endregion


    #region Methods

    private static IEnumerable<CodeClass> FindClass(CodeElements elements, string className)
    {
      foreach (CodeElement element in elements)
      {
        var codeClass = element as CodeClass;
        if (codeClass != null)
        {
          if (codeClass.Name == className)
          {
            yield return codeClass;
          }

          foreach (CodeClass innerClass in FindClass(codeClass.Members, className))
          {
            yield return innerClass;
          }
        }

        var codeNamespace = element as CodeNamespace;

        if (codeNamespace == null)
        {
          continue;
        }

        foreach (CodeClass nestedClass in FindClass(codeNamespace.Members, className))
        {
          yield return nestedClass;
        }
      }
    }

    #endregion
  }
}